// optimized-image.component.ts
import {
  Component,
  Input,
  ElementRef,
  Renderer2,
  ChangeDetectorRef,
  OnInit,
  AfterViewInit,
  OnDestroy,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  checkWebp,
  handleImageError,
  formatImageSize,
  createResponsiveImageLoader,
} from '@/utils/image-optimizer.service';
import { defaultCache } from '@/utils/cache-manager.service';

@Component({
  selector: 'app-optimized-image',
  templateUrl: './optimized-image.component.html',
  styleUrls: ['./optimized-image.component.less'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OptimizedImageComponent implements OnInit, AfterViewInit, OnDestroy {
  @Input() src!: string | Record<string, string>;
  @Input() alt = '';
  @Input() width: number | string | null = null;
  @Input() height: number | string | null = null;
  @Input() quality = 75;
  @Input() fit: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down' = 'cover';
  @Input() responsive = false;
  @Input() sizes: Record<string, string> = { sm: '100vw', md: '50vw', lg: '33vw' };
  @Input() loading: 'lazy' | 'eager' = 'lazy';
  @Input() breakpoints: Record<string, number> = {
    sm: 640, md: 768, lg: 1024, xl: 1280, '2xl': 1536,
  };
  @Input() cacheKey: string | null = null;
  @Input() cacheTTL = 24 * 60 * 60 * 1000;

  loaded = false;
  supportsWebp = false;
  imageLoader!: any;
  cachedSrc: string | null = null;
  isCaching = false;

  private mutationObserver!: MutationObserver;
  private resizeObserver!: ResizeObserver;

  constructor(
    private el: ElementRef,
    private renderer: Renderer2,
    private cdr: ChangeDetectorRef
  ) {}

  get isResponsive(): boolean {
    return this.responsive && typeof this.src === 'object';
  }

  get optimizedSrc(): string {
    let url = this.isResponsive
      ? this.imageLoader.getOptimalImageUrl(this.src)
      : (this.src as string);
    if (!this.isResponsive && this.supportsWebp && !url.endsWith('.gif')) {
      url = url.replace(/\.(jpe?g|png)$/, '.webp');
    }
    return formatImageSize(url, this.width, this.height, this.quality);
  }

  get webpSrcSet(): string {
    return this.isResponsive
      ? this.imageLoader.generateSrcSet({ ...(this.src as any), format: 'webp' })
      : '';
  }

  get originalSrcSet(): string {
    return this.isResponsive
      ? this.imageLoader.generateSrcSet(this.src as any)
      : '';
  }

  get fallbackSrc(): string {
    return this.isResponsive
      ? this.imageLoader.getOptimalImageUrl(this.src)
      : this.optimizedSrc;
  }

  get computedSizes(): string {
    return this.isResponsive
      ? this.imageLoader.generateSizes(this.sizes)
      : '';
  }

  get containerStyle(): { [key: string]: string } {
    const s: any = {};
    if (this.width)  { s.width  = typeof this.width  === 'number' ? `${this.width}px`  : this.width;  }
    if (this.height) { s.height = typeof this.height === 'number' ? `${this.height}px` : this.height; }
    return s;
  }

  get imageStyle(): { objectFit: string } {
    return { objectFit: this.fit };
  }

  get hasPlaceholder(): boolean {
    return !!this.el.nativeElement.querySelector('[placeholder]');
  }

  ngOnInit(): void {
    this.supportsWebp = checkWebp();
    this.imageLoader = createResponsiveImageLoader({
      breakpoints: this.breakpoints,
      defaultQuality: this.quality,
      formats: ['webp', 'original'],
    });
    this.loadImage();
  }

  private async loadImage(): Promise<void> {
    try {
      if (this.cacheKey) {
        const cached = await defaultCache.get(this.cacheKey);
        if (cached) {
          this.cachedSrc = cached;
          return;
        }
      }

      const img = new Image();
      img.src = this.src as string;
      await new Promise((res, rej) => {
        img.onload = res;
        img.onerror = rej;
      });

      if (this.cacheKey && !this.isCaching) {
        this.isCaching = true;
        try {
          const canvas = document.createElement('canvas');
          canvas.width = img.width;
          canvas.height = img.height;
          const ctx = canvas.getContext('2d')!;
          ctx.drawImage(img, 0, 0);
          const dataUrl = canvas.toDataURL('image/webp');
          await defaultCache.set(this.cacheKey, dataUrl, { ttl: this.cacheTTL });
          this.cachedSrc = dataUrl;
        } catch (e) {
          console.error('Failed to cache image:', e);
        }
        this.isCaching = false;
      }
    } catch (err) {
      console.error('Failed to load image:', err);
      this.renderer.listen(this.el.nativeElement, 'error', () => this.onError(err));
    }
  }

  ngAfterViewInit(): void {
    const imgEl: HTMLElement = this.el.nativeElement.querySelector('img');
    this.mutationObserver = new MutationObserver((ms) => {
      ms.forEach(m => {
        if (m.type === 'attributes' && m.attributeName === 'src') {
          if ((m.target as HTMLImageElement).complete) {
            this.markLoaded(m.target as HTMLImageElement);
          }
        }
      });
    });
    this.mutationObserver.observe(imgEl, {
      attributes: true,
      attributeFilter: ['src'],
    });

    if (this.isResponsive) {
      this.resizeObserver = new ResizeObserver(() => {
        if (this.loaded) {
          this.renderer.setAttribute(imgEl, 'src', this.optimizedSrc);
        }
      });
      this.resizeObserver.observe(document.documentElement);
    }
  }

  private markLoaded(img: HTMLImageElement): void {
    this.loaded = true;
    this.cdr.markForCheck();
  }

  onError(event: any): void {
    handleImageError(event);
  }

  ngOnDestroy(): void {
    this.mutationObserver?.disconnect();
    this.resizeObserver?.disconnect();
  }
}
